#include "processor.h"
#include "..\client\keystate.h"
#include "..\client\RC4.h"

bool DataProcessor::Process( char* pPacket, int nSize, PServerResponse pOut)
{
    FunctionLogger al(__FUNCTION__);
    if (nSize < SC_LEN || pPacket == NULL || pOut == NULL)
    {
        FLogger.information("invalid parameters for processing...");
        return error(pOut, Cmd_Invalid, SocketError);
    }

    PServerCommand pCmd = (PServerCommand)pPacket;
    int nCmdSize = pCmd->cmd_size;
    char* pData  = pCmd->data;

    if (pCmd->cmd_type == Cmd_Test)
    {
        FLogger.information("Cmd_Test received...");
        return Test(pOut);
    }
    else if (pCmd->cmd_type == Cmd_Register)
    {
        FLogger.information("Cmd_Register received...");
        return doRegistration(nCmdSize, pData, pOut);
    }
    else if (pCmd->cmd_type ==  Cmd_Heartbeat)
    {
        FLogger.information("Cmd_Heartbeat received...");
        return doHeartbeat(nCmdSize, pData, pOut);
    }
    else if (pCmd->cmd_type ==  Cmd_ChangePassword)
    {
        FLogger.information("Cmd_ChangePassword received...");
        return doChangePassword(nCmdSize, pData, pOut);
    }
    else if (pCmd->cmd_type ==  Cmd_Login)
    {
        FLogger.information("Cmd_Login received...");
        return doLogin(nCmdSize, pData, pOut);
    }
    else if (pCmd->cmd_type == Cmd_UnBind)
    {
        FLogger.information("Cmd_UnBind received...");
        return doUnBind(nCmdSize, pData, pOut);
    }
    else
    {
        for (int i = 0; i< nSize; i++)
        {
            printf("%02X ", pData[i]);
            if ( i % 8 == 0)
            {
                printf("\n");
            }
        }
        FLogger.information("Invalid command received...");
        return error(pOut);
    }
}

bool DataProcessor::doRegistration(int nSize, char* pData, PServerResponse pOut)
{
    FunctionLogger al(__FUNCTION__);
    PGeneralRequest pReq = (PGeneralRequest)pData;
    request_data rd;
    db_keys db_key;
    const int cmd = Cmd_Register;

    rd.user = pReq->username;
    rd.pass = pReq->password;
    rd.pkey = pReq->product_key;
    rd.hwid = pReq->hardware_id;
    rd.mail = pReq->email;

    PProductKeyInfo pInfo = (PProductKeyInfo)pOut->data;

    if (!DBUtils::IsProductKeyExist(rd.pkey))
    {
        FLogger.information("invalid product key");
        return error(pOut, cmd, InvalidProductKey);
    }

    if ((!DBUtils::GetProductKeyInfoByKey(rd.pkey, &db_key)))
    {
        FLogger.information("FAILED to get product key info");
        return error(pOut, cmd, SQliteError);
    }

    switch(db_key.state)
    {
    case unuse:
        return unuseHandler(&rd, pOut, &db_key);
    case registered:
        return registeredHandler(&rd,pOut, &db_key);
    case unbinded:
        return error(pOut, Cmd_Register, InvalidProductKey);
    case locked:
        return error(pOut, Cmd_Register, KeyLocked);
    case expired:
        return error(pOut, Cmd_Register, KeyExpired);
    default:
        return error(pOut);
    }
}

bool DataProcessor::doHeartbeat(int nSize, char* pData, PServerResponse pOut)
{
    FunctionLogger al(__FUNCTION__);
    PGeneralRequest pReq = (PGeneralRequest)pData;
    request_data rd;
    db_keys db_key;
    db_user u;
    const int cmd = Cmd_Heartbeat;

    rd.user = pReq->username;
    rd.pass = pReq->password;
    rd.pkey = pReq->product_key;
    rd.hwid = pReq->hardware_id;
    rd.mail = pReq->email;

    if(!DBUtils::GetProductKeyInfoByKey(rd.pkey, &db_key) || !DBUtils::GetUserInfoByKeyID(db_key.id, &u))
    {
        return error(pOut, cmd, SQliteError);
    }

    int ret = ValidateLoginInfo(&rd,&u);
    if (ret != Success)
    {
        return error(pOut, cmd, ret);
    }

    if (db_key.state == registered)
    {
        if (rd.hwid != u.hardwareid)
        {
            return error(pOut, cmd, WrongHardwareID);
        }

        return GetResponseData(cmd, rd.pkey, pOut, &u, &db_key);
    }
    else if(db_key.state == unbinded)
    {
        return error(pOut, cmd, HardwareUnBindedPleaseLogin);
    }
    else if (db_key.state == expired)
    {
        return error(pOut, cmd, KeyExpired);
    }
    else if(db_key.state == locked)
    {
        return error(pOut,cmd, KeyLocked);
    }
    
    return error(pOut, cmd, Fail);
}

bool DataProcessor::error(PServerResponse pOut,int nCmdType, int nCmdState)
{
    FunctionLogger al(__FUNCTION__);
    pOut->cmd_state = nCmdState;
    pOut->cmd_type  = nCmdType;
    pOut->data_len  = -1;
    return false;
}

bool DataProcessor::Test(PServerResponse pOut)
{
    FunctionLogger al(__FUNCTION__);
    pOut->cmd_state = Success;
    pOut->cmd_type  = Cmd_Test;
    pOut->data_len  = sizeof(ProductKeyInfo);
    PProductKeyInfo pInfo = (PProductKeyInfo)pOut->data;
    memset(pInfo, '\0', sizeof(ProductKeyInfo));
    memcpy_s(pInfo->hardware_id, sizeof(pInfo->hardware_id), "12345678123456781234567812345678", sizeof("12345678123456781234567812345678"));
    memcpy_s(pInfo->product_key, sizeof(pInfo->product_key), "87654321876543218765432187654321", sizeof("87654321876543218765432187654321"));
    pInfo->duration      = 0xFFFFFFFF;
    pInfo->generate_time = 0x11111111;
    pInfo->register_time = 0x22222222;
    pInfo->state         = unuse;

    return true;  
}

bool DataProcessor::doLogin( int nSize, char* pData, PServerResponse pOut )
{
    FunctionLogger al(__FUNCTION__);

    PGeneralRequest pGR = (PGeneralRequest)pData;
    request_data rd;
    db_keys k;
    db_user u;
    const int cmd = Cmd_Login;
    rd.user = pGR->username;
    rd.pass = pGR->password;
    rd.pkey = pGR->product_key;
    rd.hwid = pGR->hardware_id;
    rd.mail = pGR->email;


    if (!DBUtils::IsUserExist(rd.user))
    {
        return error(pOut, Cmd_Login, InexistentUser);
    }

    if (!DBUtils::GetUserInfoByUsername(rd.user, &u))
    {
        return error(pOut, Cmd_Login, SQliteError);
    }

    if (!DBUtils::IsProductKeyExist(u.key_id))
    {
        return error(pOut, Cmd_Login, InvalidProductKey);
    }

    if (!DBUtils::GetProductKeyInfoByKeyID(u.key_id, &k))
    {
        return error(pOut, Cmd_Login, SQliteError);
    }

    int ret = ValidateLoginInfo(&rd, &u);
    if (ret != Success)
    {
        return error(pOut, cmd, ret);
    }
    
    FLogger.information("user information validated...");

    if (k.state == unbinded)
    {
        FLogger.information("key is unbinded, trying to rebind...");
        if(!DBUtils::UpdateHardwareID(rd.user, rd.hwid) || !DBUtils::UpdateKeyStateWithKeyID(u.key_id, registered))
        {
            FLogger.information("update new hardware id and key state failed...");
            return error(pOut, cmd, SQliteError);
        }

        return GetResponseData(cmd, k.key, pOut, NULL,NULL);
    }
    else if (k.state == registered)
    {
        if (rd.hwid != u.hardwareid)
        {
            return error(pOut, cmd, WrongHardwareID);
        }
    }
    else if (k.state == expired)
    {
        return error(pOut, cmd, KeyExpired);
    }
    else if (k.state == locked)
    {
        return error(pOut, cmd, KeyLocked);
    }
    else if (k.state == unuse)
    {
        return error(pOut, cmd, KeyNotRegistered);
    }

    return GetResponseData(cmd, k.key, pOut, &u, &k);
}

bool DataProcessor::doChangePassword( int nSize, char* pData, PServerResponse pOut )
{
    FunctionLogger al(__FUNCTION__);
    PChangePasswordRequest pCPR = (PChangePasswordRequest)pData;
    PGeneralRequest pGR = (PGeneralRequest)&pCPR->gr;
    request_data rd;
    string new_pass = pCPR->new_pass;
    db_user u;
    const int cmd  = Cmd_ChangePassword;

    rd.user = pGR->username;
    rd.pass = pGR->password;
    rd.pkey = pGR->product_key;
    rd.hwid = pGR->hardware_id;
    rd.mail = pGR->email;

    if (!DBUtils::GetUserInfoByUsername(rd.user, &u))
    {
        return error(pOut, cmd, SQliteError);
    }

    if (Success == ValidateAllUserInfo(&rd, &u))
    {
        if(DBUtils::ChangePassword(rd.user,new_pass))
        {
            pOut->cmd_type = cmd;
            pOut->cmd_state = Success;
            return true;
        }
    }

    return error(pOut,Cmd_ChangePassword, Fail);
}

bool DataProcessor::doUnBind( int nSize, char* pData, PServerResponse pOut )
{
    FunctionLogger al(__FUNCTION__);
    PGeneralRequest pGR = (PGeneralRequest)pData;
    request_data rd;
    db_user u;
    int cmd = Cmd_UnBind;

    rd.user = pGR->username;
    rd.pass = pGR->password;
    rd.pkey = pGR->product_key;
    rd.hwid = pGR->hardware_id;
    rd.mail = pGR->email;

    if (!DBUtils::GetUserInfoByUsername(rd.user, &u))
    {
        return error(pOut, cmd, SQliteError);
    }

    int ret = ValidateUnBindInfo(&rd, &u);

    if (ret == Success)
    {
        if (DBUtils::UnBind(rd.user))
        {
            pOut->cmd_type  = cmd;
            pOut->cmd_state = Success;
            pOut->data_len  = 0;
            return true;
        }
        else
        {
            return error(pOut, Cmd_UnBind, ret);
        }
    }

    return error(pOut, cmd, ret);
}

bool DataProcessor::GetResponseData(int cmd_type, string product_key, PServerResponse pOut , db_user *pUser, db_keys* pKey)
{
    FunctionLogger al(__FUNCTION__);

    db_keys k;
    db_user u;

    if (pKey == NULL && !DBUtils::GetProductKeyInfoByKey(product_key, &k))
    {
        return error(pOut, cmd_type, SQliteError);
    }

    if(pUser == NULL && !DBUtils::GetUserInfoByKeyID(pKey == NULL ? k.id : pKey->id, &u))
    {
        return error(pOut, cmd_type, SQliteError);
    }

    PProductKeyInfo pKI = (PProductKeyInfo)pOut->data;
    if (!MakeResponseData(pKey == NULL ? &k : pKey, pUser == NULL ? &u : pUser, pKI))
    {
        return error(pOut, cmd_type, Fail);
    }

    pOut->cmd_type  = cmd_type;
    pOut->cmd_state = Success;
    pOut->data_len  = sizeof(ProductKeyInfo);
    return true;
}

bool DataProcessor::MakeResponseData( db_keys* pKey, db_user* pUser, PProductKeyInfo  pKI)
{
    FunctionLogger al(__FUNCTION__);
    if (pKey == NULL || NULL == pUser || NULL == pKI)
    {
        return false;
    }

    strcpy_s(pKI->username      , USR_LEN + 1, pUser->name.c_str());
    strcpy_s(pKI->email         , EML_LEN + 1, pUser->email.c_str());
    strcpy_s(pKI->hardware_id   , KEY_LEN + 1, pUser->hardwareid.c_str());
    strcpy_s(pKI->product_key   , KEY_LEN + 1, pKey->key.c_str());
    pKI->state              = pKey->state;
    pKI->duration           = pKey->duration;
    pKI->generate_time      = pKey->gentime;
    pKI->register_time      = pKey->regtime;
    pKI->client_type        = pKey->client_type;
    return true;
}

bool DataProcessor::unuseHandler( request_data* rd, PServerResponse pOut, db_keys* pKey )
{
    FunctionLogger al(__FUNCTION__);

    FLogger.information("the key(" + pKey->key + ") is unused...");

    int err = Success;
    int cmd = Cmd_Register;
    do 
    {

        // user name exist already
        if (DBUtils::IsUserExist(rd->user))
        {
            FLogger.information("the user already exist...");
            db_user u;
            if (!DBUtils::GetUserInfoByUsername(rd->user, &u))
            {
                FLogger.information("try to get user information...");
                err = SQliteError;
                break;
            }

            int ret = ValidateAllUserInfo(rd, &u);
            if (Success != ret)
            {
                FLogger.information("user information does not match...");
                err = ret;
                break;
            }

            FLogger.information("the user wants to use a new key,binding...");
            // user wants to use a new key
            if(!DBUtils::UpdateKeyStateWithKeyID(pKey->id, registered))
            {
                FLogger.information("failed to update key state...");
                err = SQliteError;
                break;
            }

            if (!DBUtils::UpdateUserKeyID(u.name, pKey->id))
            {
                FLogger.information("failed to update user key id...");
                err = SQliteError;
                break;
            }

            // update successfully
            FLogger.information("succeed! returning key info");
            ret = Success;
        }

        // new user, register
        if (!DBUtils::Register(rd))
        {
            FLogger.information("register failed...");
            err = SQliteError;
            break;
        }
    } while (false);

    if (err != Success)
    {
        return error(pOut, cmd, err);
    }

    return GetResponseData(cmd, pKey->key, pOut, NULL, NULL);
}

bool DataProcessor::registeredHandler( request_data* rd, PServerResponse pOut, db_keys* pKey )
{
    FunctionLogger al(__FUNCTION__);
    db_user u;
    int cmd = Cmd_Register;

    if (!DBUtils::GetUserInfoByKeyID(pKey->id,&u))
    {
        return error(pOut, cmd, SQliteError);
    }

    int ret = ValidateAllUserInfo(rd, &u);
    if (ret != Success)
    {
        return error(pOut, cmd, ret);
    }

    return GetResponseData(cmd, pKey->key, pOut, &u, pKey);
}

int DataProcessor::ValidateAllUserInfo(request_data* pRD, db_user* pUser)
{
    if (pRD->user != pUser->name)
    {
        return InexistentUser;
    }

    if (pRD->pass != pUser->pass)
    {
        return WrongPassword;
    }

    if (pRD->mail != pUser->email)
    {
        return WrongEmail;
    }

    if (pRD->hwid != pUser->hardwareid)
    {
        return WrongHardwareID;
    }

    return Success;
}

int DataProcessor::ValidateLoginInfo( request_data* pRD, db_user* pUser )
{
    if (pRD->pass != pUser->pass)
    {
        return WrongPassword;
    }

    return Success;
}

int DataProcessor::ValidateUnBindInfo( request_data* pRD, db_user* pUser )
{
    if (pRD->pass != pUser->pass)
    {
        return WrongPassword;
    }

    if (pRD->mail != pUser->email)
    {
        return WrongEmail;
    }

    if (!DBUtils::IsProductKeyExist(pUser->key_id))
    {
        return NoAvailableKeyBinded;
    }

    return Success;
}
